"""
Navigation controller for interactive exploration.

Handles navigation stack management, breadcrumb tracking,
and path optimization for manual exploration sessions.
"""

from typing import List, Dict, Any, Optional, Tuple
from dataclasses import dataclass
from datetime import datetime

from ...core.base_mcp import CallTreeNode
from ..utils.colors import ColorPrinter, Color


@dataclass
class NavigationStep:
    """Represents a single step in the navigation history."""
    node: CallTreeNode
    timestamp: datetime
    user_action: str  # 'navigate', 'back', 'select', etc.
    selection_weight: float = 1.0
    notes: str = ""


class NavigationController:
    """Manages navigation state and history for exploration sessions."""
    
    def __init__(self):
        self.navigation_stack: List[NavigationStep] = []
        self.history: List[NavigationStep] = []
        self.bookmarks: Dict[str, CallTreeNode] = {}
        self.printer = ColorPrinter()
    
    def push_node(self, node: CallTreeNode, action: str = "navigate", weight: float = 1.0, notes: str = "") -> None:
        """Push a new node onto the navigation stack."""
        step = NavigationStep(
            node=node,
            timestamp=datetime.utcnow(),
            user_action=action,
            selection_weight=weight,
            notes=notes
        )
        
        self.navigation_stack.append(step)
        self.history.append(step)
    
    def pop_node(self) -> Optional[CallTreeNode]:
        """Pop the current node and return to parent."""
        if len(self.navigation_stack) > 1:
            popped_step = self.navigation_stack.pop()
            
            # Add back action to history
            back_step = NavigationStep(
                node=self.current_node,
                timestamp=datetime.utcnow(),
                user_action="back",
                notes=f"Returned from {popped_step.node.name}"
            )
            self.history.append(back_step)
            
            return self.current_node
        return None
    
    @property
    def current_node(self) -> Optional[CallTreeNode]:
        """Get the current node."""
        if self.navigation_stack:
            return self.navigation_stack[-1].node
        return None
    
    @property
    def current_depth(self) -> int:
        """Get current navigation depth."""
        return len(self.navigation_stack)
    
    @property
    def parent_node(self) -> Optional[CallTreeNode]:
        """Get the parent node."""
        if len(self.navigation_stack) > 1:
            return self.navigation_stack[-2].node
        return None
    
    def get_breadcrumb_path(self) -> List[str]:
        """Get breadcrumb path as list of node names."""
        return [step.node.name for step in self.navigation_stack]
    
    def print_breadcrumb(self):
        """Print current breadcrumb path."""
        if not self.navigation_stack:
            self.printer.print_info("No navigation path")
            return
        
        path_parts = []
        for i, step in enumerate(self.navigation_stack):
            node_name = step.node.name
            if i == len(self.navigation_stack) - 1:
                # Current node - highlight
                path_parts.append(self.printer.colorize(node_name, Color.BRIGHT_WHITE))
            else:
                path_parts.append(self.printer.colorize(node_name, Color.BRIGHT_BLACK))
        
        breadcrumb = " → ".join(path_parts)
        self.printer.print_colored(f"Path: {breadcrumb}", Color.CYAN)
    
    def print_navigation_tree(self, max_depth: int = 5):
        """Print navigation path as a tree structure."""
        if not self.navigation_stack:
            return
        
        self.printer.print_header("Navigation Tree", level=3)
        
        for i, step in enumerate(self.navigation_stack):
            if i >= max_depth:
                self.printer.print_colored("  " * i + "...", Color.BRIGHT_BLACK)
                break
            
            node = step.node
            is_current = i == len(self.navigation_stack) - 1
            
            # Create tree prefix
            if i == 0:
                prefix = "🏠 "  # Root
            else:
                prefix = "  " * (i - 1) + "└─ "
            
            # Format node info
            node_info = f"{node.name}"
            if hasattr(node, 'file_path'):
                from pathlib import Path
                node_info += f" ({Path(node.file_path).name}:{node.line_number})"
            
            # Add action info
            if step.user_action != "navigate":
                node_info += f" [{step.user_action}]"
            
            if step.selection_weight != 1.0:
                node_info += f" (weight: {step.selection_weight:.2f})"
            
            # Color based on current status
            color = Color.BRIGHT_WHITE if is_current else Color.WHITE
            self.printer.print_colored(f"{prefix}{node_info}", color)
    
    def add_bookmark(self, name: str, node: Optional[CallTreeNode] = None):
        """Add a bookmark for quick navigation."""
        target_node = node or self.current_node
        if target_node:
            self.bookmarks[name] = target_node
            self.printer.print_success(f"Bookmark '{name}' added for {target_node.name}")
        else:
            self.printer.print_error("No node to bookmark")
    
    def navigate_to_bookmark(self, name: str) -> Optional[CallTreeNode]:
        """Navigate to a bookmarked node."""
        if name in self.bookmarks:
            target_node = self.bookmarks[name]
            self.push_node(target_node, action="bookmark", notes=f"Navigated to bookmark '{name}'")
            return target_node
        else:
            self.printer.print_error(f"Bookmark '{name}' not found")
            return None
    
    def list_bookmarks(self):
        """List all bookmarks."""
        if not self.bookmarks:
            self.printer.print_info("No bookmarks saved")
            return
        
        self.printer.print_header("Bookmarks", level=3)
        for name, node in self.bookmarks.items():
            node_info = f"{node.name}"
            if hasattr(node, 'file_path'):
                from pathlib import Path
                node_info += f" ({Path(node.file_path).name}:{node.line_number})"
            
            self.printer.print_key_value(name, node_info)
    
    def get_navigation_statistics(self) -> Dict[str, Any]:
        """Get navigation statistics."""
        if not self.history:
            return {'total_steps': 0}
        
        # Count actions
        action_counts = {}
        for step in self.history:
            action_counts[step.user_action] = action_counts.get(step.user_action, 0) + 1
        
        # Calculate timing
        start_time = self.history[0].timestamp
        end_time = self.history[-1].timestamp
        total_duration = (end_time - start_time).total_seconds()
        
        # Get unique nodes visited
        unique_nodes = set(step.node.name for step in self.history)
        
        # Calculate depth statistics
        depths = [len(self.navigation_stack[:i+1]) for i in range(len(self.navigation_stack))]
        max_depth = max(depths) if depths else 0
        avg_depth = sum(depths) / len(depths) if depths else 0
        
        return {
            'total_steps': len(self.history),
            'unique_nodes_visited': len(unique_nodes),
            'action_counts': action_counts,
            'total_duration_seconds': total_duration,
            'current_depth': self.current_depth,
            'max_depth_reached': max_depth,
            'average_depth': avg_depth,
            'bookmarks_count': len(self.bookmarks)
        }
    
    def print_navigation_summary(self):
        """Print summary of navigation session."""
        stats = self.get_navigation_statistics()
        
        self.printer.print_header("Navigation Summary", level=2)
        
        summary_data = {
            'Total Steps': stats['total_steps'],
            'Unique Nodes': stats['unique_nodes_visited'],
            'Current Depth': stats['current_depth'],
            'Max Depth': stats['max_depth_reached'],
            'Duration': f"{stats['total_duration_seconds']:.1f} seconds",
            'Bookmarks': stats['bookmarks_count']
        }
        
        for key, value in summary_data.items():
            self.printer.print_key_value(key, str(value))
        
        # Action breakdown
        if stats.get('action_counts'):
            print()
            self.printer.print_header("Actions Breakdown", level=3)
            for action, count in stats['action_counts'].items():
                self.printer.print_key_value(action.title(), count)
    
    def find_path_to_node(self, target_node_name: str) -> Optional[List[CallTreeNode]]:
        """Find path from current node to target node name."""
        # This would implement pathfinding in the call tree
        # For now, return None (placeholder)
        return None
    
    def suggest_next_nodes(self, current_node: CallTreeNode, limit: int = 3) -> List[Tuple[CallTreeNode, str]]:
        """Suggest next nodes to explore based on navigation history."""
        suggestions = []
        
        if not current_node.callee_nodes:
            return suggestions
        
        # Simple suggestion logic based on node characteristics
        for callee in current_node.callee_nodes[:limit]:
            reason = "Available function"
            
            # Check if node has high complexity
            if hasattr(callee, 'complexity_score') and callee.complexity_score > 7:
                reason = "High complexity - might be interesting"
            
            # Check if node hasn't been visited
            visited_names = {step.node.name for step in self.history}
            if callee.name not in visited_names:
                reason = "Unvisited function"
            
            # Check if node has many callees
            if len(callee.callee_nodes) > 5:
                reason = "Hub function - calls many others"
            
            suggestions.append((callee, reason))
        
        return suggestions
    
    def print_suggestions(self, suggestions: List[Tuple[CallTreeNode, str]]):
        """Print exploration suggestions."""
        if not suggestions:
            return
        
        self.printer.print_header("Exploration Suggestions", level=3)
        
        for i, (node, reason) in enumerate(suggestions):
            node_info = f"{node.name}"
            if hasattr(node, 'complexity_score'):
                node_info += f" (complexity: {node.complexity_score:.1f})"
            
            self.printer.print_colored(f"  {i+1}. {node_info}", Color.BRIGHT_GREEN)
            self.printer.print_colored(f"     {reason}", Color.BRIGHT_BLACK)
    
    def reset_navigation(self):
        """Reset navigation to start fresh."""
        if self.navigation_stack:
            root_step = self.navigation_stack[0]
            self.navigation_stack = [root_step]
            
            # Record reset action
            reset_step = NavigationStep(
                node=root_step.node,
                timestamp=datetime.utcnow(),
                user_action="reset",
                notes="Navigation reset to root"
            )
            self.history.append(reset_step)
    
    def can_go_back(self) -> bool:
        """Check if back navigation is possible."""
        return len(self.navigation_stack) > 1
    
    def export_navigation_path(self) -> List[Dict[str, Any]]:
        """Export navigation path for saving/analysis."""
        return [
            {
                'node_name': step.node.name,
                'file_path': step.node.file_path,
                'line_number': step.node.line_number,
                'timestamp': step.timestamp.isoformat(),
                'action': step.user_action,
                'weight': step.selection_weight,
                'notes': step.notes
            }
            for step in self.history
        ]
    
    def import_navigation_path(self, path_data: List[Dict[str, Any]]):
        """Import navigation path from saved data."""
        # This would reconstruct the navigation state
        # Placeholder implementation
        self.printer.print_info(f"Would import {len(path_data)} navigation steps")
    
    def get_backtrack_options(self) -> List[Tuple[int, CallTreeNode]]:
        """Get options for backtracking to previous nodes."""
        options = []
        
        for i in range(len(self.navigation_stack) - 2, -1, -1):
            step = self.navigation_stack[i]
            options.append((i, step.node))
        
        return options
    
    def backtrack_to_depth(self, target_depth: int) -> Optional[CallTreeNode]:
        """Backtrack to specific depth in navigation stack."""
        if target_depth < 0 or target_depth >= len(self.navigation_stack):
            return None
        
        # Remove steps beyond target depth
        steps_to_remove = len(self.navigation_stack) - target_depth - 1
        for _ in range(steps_to_remove):
            self.navigation_stack.pop()
        
        # Record backtrack action
        backtrack_step = NavigationStep(
            node=self.current_node,
            timestamp=datetime.utcnow(),
            user_action="backtrack",
            notes=f"Backtracked to depth {target_depth}"
        )
        self.history.append(backtrack_step)
        
        return self.current_node